'use strict';

const tool = require('./utils/tool');
const validate = require('./utils/validate');
const Path = require('path')
const { fileExist, writeData, readData, removeFile } = require('../../utils/file')

module.exports = {

  /**
   * 自定义封装GET
   * @param {*} url 请求地址
   * @param {*} data 请求参数
   * @param {*} other 其他
   */
  $wGet(url, data = {}, other = {}) {
    return this.curl(url, {
      dataType: 'json',
      method: 'GET',
      data,
      ...other,
    });
  },

  /**
   * 自定义封装POST
   * @param {*} url 请求地址
   * @param {*} data 请求参数
   */
  $wPost(url, data = {}, other = {}) {
    return this.curl(url, {
      dataType: 'json',
      method: 'POST',
      data,
      ...other
    });
  },

  /**
   * 响应html文件
   * @param {*} fileName 文件名
   */
  sendHtmlFile(fileName) {
    this.response.sendHtmlFile(fileName);
  },

  /**
   * 发送正确数据
   * @param {*} data 要发送的数据
   * @param {*} msg 要传输的额外信息
   */
  sendSuccess(data = {}, msg = '') {
    this.response.sendSuccess(data, msg);
  },

  /**
   * 获取请求体
   */
  w_body(keys) {
    const { request } = this
    const { body } = request
    if (keys) {
      const newKeys = typeof keys === 'string' ? keys.split(' ') : [...keys]
      const json = {}
      newKeys.forEach((key) => {
        if (body[key] !== undefined) {
          json[key] = body[key]
        }
      })
      return json
    }
    return body
  },

  /**
   * 获取数据
   * @param {*} key 缓存的键值
   * @param {*} getDataFunc 获取数据的方法，异步方法，如果没有缓存就会执行当前方法
   * @param {*} isLocal 是否本地直接缓存数据，如果是返回304必须是浏览器第一次请求过数据，每个人都会缓存自己的
   *                    如果是本地缓存。直接缓存数据，所有人请求都会获取缓存的数据
   */
  async getCache(key, getDataFunc, isLocal = false) {
    const { req } = this
    const { redis } = this.app
    // 获取缓存的数据
    const serverCatchData = await redis.get(key);
    if (serverCatchData) { // 判断是否有缓存的数据
      if (isLocal) {  // 是否是本地直接缓存数据
        try {
          // 将数据反序列化拿到
          return JSON.parse(serverCatchData).data
        } catch {
          // 有可能获取的数据是有问题的，不能反序列化导致报错，直接删除掉当前redis存储的数据
          await redis.del(key);
        }
      } else {
        // 获取客户端时间
        const clientDate = req.headers['if-modified-since']
        // 如果客户端的时间等于缓存的时间。直接返回304状态，获取浏览器缓存的数据
        if (clientDate === serverCatchData) {
          this.throw(304)
        }
      }
    }
    const resData = await getDataFunc()
    let catchData = ''
    if (isLocal) {
      catchData = JSON.stringify({
        data: resData
      })
    } else {
      // 首先判断redis是否缓存了时间，因为有可能是第1个人已经缓存了时间,
      // 那么第二个人进来时间应该是保持不变，如果改变缓存时间，即便数据不变，第一个人再次获取仍然不是缓存数据
      catchData = serverCatchData || new Date().toGMTString()
      // 设置请求头缓存
      this.set('Last-Modified', catchData)
      this.set('Cache-Control', 'no-cache')
    }
    // 如果304缓存并发现有已经有缓存的数据，就没必要你再次到redis缓存，因为相等
    if (isLocal || (!isLocal && !serverCatchData)) {
      await redis.set(key, catchData, 'EX', 1000 * 3600 * 24 * 7);
    }
    return resData
  },

  /**
   * 获取列表缓存数据
   * @param {*} key 缓存的键值
   * @param {*} getDataFunc 获取数据的方法，异步方法，如果没有缓存就会执行当前方法
   */
  async getListCache(key, getDataFunc) {
    const { req } = this
    const { redis } = this.app
    // 获取请求参数序列化
    const queryKey = JSON.stringify({ ...this.query })
    // 获取客户端时间
    const clientDate = req.headers['if-modified-since']
    // 获取服务端缓存时间
    const serverCatchDate = await redis.hget(key, queryKey);
    // 如果服务端有数据，并且等于客户端带过来的时间，就是有缓存的数据
    if (serverCatchDate && clientDate === serverCatchDate) {
      this.throw(304)
    }
    // 没有缓存，获取数据
    const resData = await getDataFunc()
    // 缓存时间
    const dataTimeStr = serverCatchDate || new Date().toGMTString()
    this.set('Last-Modified', dataTimeStr)
    this.set('Cache-Control', 'no-cache')
    // 如果之前已经缓存了时间就没必要再次缓存
    if (!serverCatchDate) {
      await redis.hset(key, queryKey, dataTimeStr);
    }
    return resData
  },

  /**
   * 清除缓存
   * 添加、删除数据的时候需要清除掉列表缓存
   * 修改数据的时候需要清除掉列表缓存数据的跟详情缓存
   * @param {*} data 缓存的KEY
   */
  async clearCache(...data) {
    const { redis } = this.app
    await redis.del(...data);
  },

  /**
   * 获取本地缓存的路径
   * @param key
   * @returns {string}
   */
  getJsonLocalCatchPath(key) {
    return Path.resolve(__dirname, `../public/json/${key}.json`)
  },

  /**
   * 获取JSON静态缓存
   * @param key
   * @param getDataFunc
   * @param config
   * @returns {Promise<void>}
   */
  async getJsonLocalCatch(key, getDataFunc, config) {
    const path = this.getJsonLocalCatchPath(key)
    const isExist = await fileExist(path)
    if (isExist) {
      try {
        const data = await readData(path)
        return JSON.parse(data.toString()).data
      } catch (err) {
        this.clearJsonLocalCatch(path)
      }
    }

    const data = await getDataFunc()
    await writeData(path, {data})
    return data
  },

  /**
   * 清除缓存
   * @param key
   * @returns {Promise<void>}
   */
  async clearJsonLocalCatch(key) {
    const path = this.getJsonLocalCatchPath(key)
    await removeFile(path)
  },

  w_validate_param_id() {
    this.validate({
      id: {
        type: 'mongo-id',
      },
    }, this.params);
  },

  // 是否登录
  isLogin() {
    return !!this.session.userModel
  },

  async addComments(modelClassName) {
    this.w_validate_param_id()
    this.validate({
      icon: {
        type: 'string',
        max: 200
      },
      name: {
        type: 'string',
        max: 20
      },
      userId: {
        type: 'string',
        max: 50
      },
      mention: {
        type: 'string',
        max: 20,
        required: false,
        default: ''
      },
      content: {
        type: 'string',
        max: 400
      },
      superCommentId: {
        required: false,
        type: 'mongo-id',
      }
    });
    await this.service.comments.addComments(this.params.id, this.w_body(), modelClassName);
    this.sendSuccess();
  },

  // 删除评论
  async deleteComments(modelClassName) {
    this.w_validate_param_id()
    this.validate({
      superCommentId: {
        type: 'mongo-id',
        required: false,
      },
      dataId: {
        type: 'mongo-id',
      }
    }, this.query);
    await this.service.comments.deleteComments(this.params.id, this.query, modelClassName);
    this.sendSuccess();
  },

  /**
   * 自定义工具类
   */
  w_tool: tool,

  /**
   * 自定义校验类
   */
  ...validate,
};
